<?php

namespace app\core\util;

use app\core\Util;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use think\facade\Cache;

/**
 * @extends Util<Token>
 */
class Token extends Util
{
    /**
     * 用户标识
     * @var int|string
     */
    protected $userId;

    /**
     * 会员标识
     * @var int|string
     */
    protected $memberId;

    /**
     * 会员标识
     * @var int|string
     */
    protected $templeId;

    /**
     * 用户信息
     * @var null|array|string|int
     */
    protected $userInfo = null;

    /**
     * 成员信息
     * @var null|array|string|int
     */
    protected $memberInfo = null;

    /**
     * 成员信息
     * @var null|array|string|int
     */
    protected $templeInfo = null;
    /**
     * token 占线
     * @var bool
     */
    protected $isBusy = true;

    /**
     * 签名配置
     * @var array
     */
    protected $config = [
        'alg' => 'HS256',
        'key' => '123456',// 签名密钥
        'expire' => 2592000,// 有效期
    ];

    public function __construct($config = [])
    {
        $defaultConfig = [
            'alg' => config('token.alg', 'HS256'),
            'key' => config('token.key', '123456'),
            'expire' => config('token.expire', 2592000)
        ];

        $this->config = array_merge($this->config, $defaultConfig, $config);
    }

    /**
     * 生成 Token
     * @return string
     */
    public function genToken(): string
    {
        $config = $this->config;
        $time = time();
        $data = [
            'iat' => $time, // 签发时间
            'nbf' => $time, // 某个时间点后才能访问，比如设置time+30，表示当前时间30秒后才能使用
            'exp' => $time + $config['expire'], // 过期时间,这里设置2个小时
            'data' => [
                'user_id' => $this->userId,
                'time' => $time,
            ]
        ];
        // 缓存用户信息
        $cacheData = [
            'time' => $time,
            'userInfo' => $this->userInfo
        ];
        Cache::set($this->userId, json_encode($cacheData), $config['expire'] + 2592000);
        return JWT::encode($data, $config['key'], $config['alg']);
    }

    /**
     * 生成 Token
     * @return string
     */
    public function genMemberToken(): string
    {
        $config = $this->config;
        $time = time();
        $data = [
            'iat' => $time, // 签发时间
            'nbf' => $time, // 某个时间点后才能访问，比如设置time+30，表示当前时间30秒后才能使用
            'exp' => $time + $config['expire'], // 过期时间,这里设置2个小时
            'data' => [
                'member_id' => $this->memberId,
                'time' => $time,
            ]
        ];
        // 缓存用户信息
        $cacheData = [
            'time' => $time,
            'memberInfo' => $this->memberInfo
        ];
        Cache::set($this->memberId, json_encode($cacheData), $config['expire'] + (30 * 86400));
        return JWT::encode($data, $config['key'], $config['alg']);
    }

    /**
     * 生成 Token
     * @return string
     */
    public function genTempleToken(): string
    {
        $config = $this->config;
        $time = time();
        $data = [
            'iat' => $time, // 签发时间
            'nbf' => $time, // 某个时间点后才能访问，比如设置time+30，表示当前时间30秒后才能使用
            'exp' => $time + $config['expire'], // 过期时间,这里设置2个小时
            'data' => [
                'temple_id' => $this->templeId,
                'time' => $time,
            ]
        ];
        // 缓存用户信息
        $cacheData = [
            'time' => $time,
            'templeInfo' => $this->templeInfo
        ];
        Cache::set($this->templeId, json_encode($cacheData), $config['expire'] + (30 * 86400));
        return JWT::encode($data, $config['key'], $config['alg']);
    }

    public function delMemberToken($memberId): string
    {
        $this->setMemberId($memberId);
        $cacheData = Cache::get($this->memberId);
        if (empty($cacheData)) {
            return false;
        }
        Cache::delete($this->memberId);
        return true;
    }

    public function delTempleToken($templeId): string
    {
        $this->setMemberId($templeId);
        $cacheData = Cache::get($this->templeId);
        if (empty($cacheData)) {
            return false;
        }
        Cache::delete($this->templeId);
        return true;
    }

    /**
     * 刷新用户信息
     * @param $userInfo
     * @return void
     */
    public function refreshUserInfo($userInfo)
    {
        $cacheData = Cache::get($this->userId);
        if (empty($cacheData)) {
            throw new ExpiredException('token 不存在或失效');
        }
        $cacheJson = json_decode($cacheData, JSON_UNESCAPED_UNICODE);
        $cacheJson['userInfo'] = $userInfo;
        Cache::set($this->userId, json_encode($cacheJson), $this->config['expire'] + 2592000);
    }

    /**
     * 刷新用户信息
     * @param $userInfo
     * @return void
     */
    public function refreshMemberInfo($memberInfo)
    {
        $cacheData = Cache::get($this->memberId);
        if (empty($cacheData)) {
            throw new ExpiredException('token 不存在或失效');
        }
        $cacheJson = json_decode($cacheData, JSON_UNESCAPED_UNICODE);
        $cacheJson['memberInfo'] = $memberInfo;
        Cache::set($this->memberId, json_encode($cacheJson), $this->config['expire'] + 2592000);
    }

    /**
     * 刷新用户信息
     * @param $userInfo
     * @return void
     */
    public function refreshTempleInfo($templeInfo)
    {
        $cacheData = Cache::get($this->memberId);
        if (empty($cacheData)) {
            throw new ExpiredException('token 不存在或失效');
        }
        $cacheJson = json_decode($cacheData, JSON_UNESCAPED_UNICODE);
        $cacheJson['templeInfo'] = $templeInfo;
        Cache::set($this->templeId, json_encode($cacheJson), $this->config['expire'] + 2592000);
    }

    /**
     * 验证 Token
     * @param string $jwt
     * @return array
     * @throws \Exception
     */
    public function verifyToken(string $jwt): array
    {
        $key = $this->config['key'];
        JWT::$leeway = 60 * 24;
        $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
        $jwtData = (array)((array)$decoded)['data'];
        $cacheData = Cache::get($jwtData['user_id']);
        if (empty($cacheData)) {
            throw new ExpiredException('token 不存在或失效');
        }
        $cacheJson = json_decode($cacheData, JSON_UNESCAPED_UNICODE);
        if ($cacheJson['time'] != $jwtData['time'] && $this->isBusy) {
            throw new ExpiredException('token 失效');
        }
        return $cacheJson['userInfo'];
    }

    /**
     * 验证 Token
     * @param string $jwt
     * @return array
     * @throws \Exception
     */
    public function verifyMemberToken(string $jwt): array
    {
        $key = $this->config['key'];
        JWT::$leeway = 60 * 24;
        $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
        $jwtData = (array)((array)$decoded)['data'];
        $cacheData = Cache::get($jwtData['member_id']);
        if (empty($cacheData)) {
            throw new ExpiredException('token 不存在或失效');
        }
        $cacheJson = json_decode($cacheData, JSON_UNESCAPED_UNICODE);
        if ($cacheJson['time'] != $jwtData['time'] && $this->isBusy) {
            throw new ExpiredException('token 失效');
        }
        return $cacheJson['memberInfo'];
    }

    /**
     * 验证 Token
     * @param string $jwt
     * @return array
     * @throws \Exception
     */
    public function verifyTempleToken(string $jwt): array
    {
        $key = $this->config['key'];
        JWT::$leeway = 60 * 24;
        $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
        $jwtData = (array)((array)$decoded)['data'];
        $cacheData = Cache::get($jwtData['temple_id']);
        if (empty($cacheData)) {
            throw new ExpiredException('token 不存在或失效');
        }
        $cacheJson = json_decode($cacheData, JSON_UNESCAPED_UNICODE);
        if ($cacheJson['time'] != $jwtData['time'] && $this->isBusy) {
            throw new ExpiredException('token 失效');
        }
        return $cacheJson['templeInfo'];
    }

    /**
     * 设置 userInfo
     * @param array|string|int $userInfo
     * @return Token
     */
    public function setUserInfo($userInfo): self
    {
        $this->userInfo = $userInfo;
        return $this;
    }

    /**
     * 设置 MemberInfo
     * @param array|string|int $memberInfo
     * @return Token
     */
    public function setMemberInfo($memberInfo): self
    {
        $this->memberInfo = $memberInfo;
        return $this;
    }

    /**
     * 设置 TempleInfo
     * @param array|string|int $templeInfo
     * @return Token
     */
    public function setTempleInfo($templeInfo): self
    {
        $this->templeInfo = $templeInfo;
        return $this;
    }

    /**
     * 设置 userId
     * @param int|string $userId
     * @return Token
     */
    public function setUserId($userId): self
    {
        $this->userId = "user:" . $userId;
        return $this;
    }

    /**
     * 设置 memberId
     * @param int|string $memberId
     * @return Token
     */
    public function setMemberId($memberId): self
    {
        $this->memberId = "member:" . $memberId;
        return $this;
    }

    /**
     * 设置 memberId
     * @param int|string $templeId
     * @return Token
     */
    public function setTempleId($templeId): self
    {
        $this->templeId = "temple:" . $templeId;
        return $this;
    }

    /**
     * @param bool $isBusy
     * @return Token
     */
    public function setBusy(bool $isBusy): self
    {
        $this->isBusy = $isBusy;
        return $this;
    }
}